# -*-coding:utf-8-*-

# 变量
variable = '变量'
_MAX = 'protect变量'  # 防止通过from a import *的形式导入,但可以from a import _MAX或import 模块._MAX强行导入
__all__ = ['variable', 'function', 'A']  # 声明允许通过from a import *的形式导入的变量

# 函数
def function():
    print(variable)
"""
外层的def是函数(function)
类中的def是方法(method)
(1) xx: 公有属性或方法
(2) _xx: 前置单下划线,私有化属性或方法.一般来讲，变量名_xx被看作是“私有的”,在模块或类外不可以使用,类对象和子类可以访问.
         这并不能完全做到真正的私有，只是约定俗成的而已,外部依旧可以通过_XX访问
(3) __xx: 前置双下划线,私有化属性或方法,无法在外部直接访问(名字重整成"_类名__xx"所以访问不到,只能是允许这个类本身进行访问了.连子类也不可以).
(4) __xx__: 前后双下划线,系统定义属性或方法(如模块.__dict__,类名.__name__,魔术方法等),对Python来说有特殊含义,普通的变量应避免这种命名风格.
(5) xx_: 后置单下划线,用于避免与Python关键词的冲突.
"""
class A(object):
    # 类变量，相当于java的静态变量
    field = '类变量'
    __xx__ = '1'

    def __init__(self):
        # 实例变量，相当于java的类成员变量
        self.a = '实例变量'
        self._b = '私有实例变量'
        # 类内部调用自身或父类方法的方式
        self.instance_method()
        self.class_method()
        self.static_method()

    # 实例方法，相当于java的类成员方法
    def instance_method(self):
        print('实例函数', self.field)

    # 类方法，相当于java的静态方法(可通过A.class_method()或a.class_method()调用)，内部调用类变量时可以使用cls.类变量的形式
    @classmethod
    def class_method(cls):
        print('类函数', cls.field)

    # 静态方法，相当于java的静态方法(可通过A.class_method()或a.class_method()调用)，内部调用类变量时必须使用类名.类变量的形式
    @staticmethod
    def static_method():
        print('静态函数', A.field)

    # @property相当于getter
    @property
    def b(self):
        return self._b

    # @x.setter相当于setter,getter和setter的方法名都要跟x相同,通过"实例.x=a,实例.x"形式进行私有变量的赋值和获取
    @b.setter
    def b(self, value):
        self._b = value

    # 调用不存在的实例变量时触发(类变量不会触发)
    def __getattr__(self, item):
        print('__getattr__', item)
        return 9999

    # 设置实例变量或者给已存在的实例变量赋值时触发(__init__中的变量定义赋值操作也会触发,类变量不会触发)
    def __setattr__(self, key, value):
        super().__setattr__(key, value)
        print('__setattr__', key, value)

    # 调用实例的变量时触发(类变量不会触发),调用不存在的实例变量时会进一步调用__getattr__
    def __getattribute__(self, item):
        print('__getattribute__', item)
        return super().__getattribute__(item)

    # 使用in关键字判断是否存在时触发('实例' in A())
    def __contains__(self, item):
        return item in self.a

    # 该方法的功能类似于在类中重载 () 运算符，使得类实例对象可以像调用普通函数那样，以“对象名()”的形式使用
    def __call__(self, *args, **kwargs):
        return args, kwargs

if __name__ == '__main__':
    a = A()
    print(('实例' in a))  # contains
    print(a(1, 2, key=3))